/*
 *  VortPolygon.cpp
 *  VortPan
 *
 *  Created by Gregor Veble on 6/16/10.
 *  Copyright 2010  . All rights reserved.
 *
 */

#include "VortPolygon.h"

using namespace VortPan;

VortPolygon::VortPolygon(bool IsWake) : WakePoly(IsWake), SolidIndex(-1), pSibling(this), Normal(e3()), Area(ZERO) {
}

VortPolygon::~VortPolygon(){
}

void VortPolygon::AddPoint(VortPointReference I){
	PointList.push_back(I);
}

int VortPolygon::AddPoint(VortPointReference I, int BeforePoint){
	int Size=PointList.size();
	if (Size<=0) {
		AddPoint(I);
		return 1;
	}
	
	if (BeforePoint>=PointList.size()) {
		PointList.push_back(I);
	} else {
		if(BeforePoint<0){
			BeforePoint=0;
		}
		
		PointReferenceVector::iterator it=PointList.begin()+BeforePoint;
		PointList.insert(it,I);
	}	
	return 0;
}

void VortPolygon::SetCenterPoint(VortPoint Center){
	CenterPoint=Center;
}

int VortPolygon::RecalculateAverageCenterPoint(re Eps){
	int Size;
	
	RecalculateNormal();
	
	Size=PointList.size();
	if(Size>0){
		CenterPoint.Position=V0();
		for(int i=0;i<Size;i++){
			CenterPoint.Position=CenterPoint.Position+PointList[i].pPoint->Position;
		}
		CenterPoint.Position=CenterPoint.Position/Size;
		CenterPoint.Position=CenterPoint.Position+Eps*Normal;
		return 0;
	} else {
		return -1;
	}
}

void VortPolygon::RecalculateNormal(){
	int Size=PointList.size();
	Normal=V0();
	Vector3D& Pos0=PointList[0].pPoint->Position;
	for (int i=2; i<Size; i++) {
		Normal=Normal+Vectorial(PointList[i-1].pPoint->Position-Pos0, PointList[i].pPoint->Position-Pos0);
	}
	Area=Norm(Normal)*HALF;
	Normal=Normalize(Normal);
}

re& VortPolygon::GetArea(){
	return Area;
}


re VortPolygon::SolidAngle(const Vector3D& R, const Vector3D& V1, const Vector3D& V2, const Vector3D& V3){
	Vector3D R1=V1-R;
	Vector3D R2=V2-R;
	Vector3D R3=V3-R;
	
	re rd1=Norm(R1);
	re rd2=Norm(R2);
	re rd3=Norm(R3);
	
	re res=TWO*ATAN2(
					 R1.v[0]*(R2.v[1]*R3.v[2]-R2.v[2]*R3.v[1])+
					 R1.v[1]*(R2.v[2]*R3.v[0]-R2.v[0]*R3.v[2])+
					 R1.v[2]*(R2.v[0]*R3.v[1]-R2.v[1]*R3.v[0]),
					 rd1*rd2*rd3 + rd1*(R2*R3) + rd2*(R3*R1) + rd3*(R1*R2)
					 );
	
	return res;
}


Vector3D VortPolygon::VortexVelocity(const Vector3D& R1, const Vector3D& R2, const Vector3D& R, const re& InvCoreSize2){
	Vector3D E0,E1,E2,D;
	re N0,N1,N2,c1,c2,d2;
	
	E0=R2-R1;
	E1=R-R1;
	E2=R-R2;
	
	N0=Norm(E0);
	N1=Norm(E1);
	N2=Norm(E2);
	
	if((N1>0)&&(N2>0)&&(N0>0)){
		E0=E0/N0;
		c1=(E1*E0)/N1;
		c2=(E2*E0)/N2;
		D=Vectorial(E0,E1);
		d2=Square(D);
		if(d2>0){
			return (ONE_OVER_FOUR_PI*(c2-c1)*(ONE-exp(-HALF*d2*InvCoreSize2))/d2)*D;
		} else {
			return V0();
		}
	} else {
		return V0();
	}
}

Vector3D VortPolygon::GetRegularizedVelocityInfluence(const Vector3D& R, const re& InvCoreSize2){
	int Size=PointList.size();
	Vector3D Res=V0();
	for (int i=0; i<Size; i++) {
		Res=Res-VortexVelocity(PointList[i].pPoint->Position, PointList[(i+1)%Size].pPoint->Position, R, InvCoreSize2);
	}
	return Res;
}


re VortPolygon::GetSolidAngle(const Vector3D& R){
	int Size=PointList.size();
	re Solid=0;
	for (int i=2; i<Size; i++) {
		Solid+=SolidAngle(R,PointList[0].pPoint->Position,PointList[i-1].pPoint->Position,PointList[i].pPoint->Position);
	}
	return Solid;
}

re VortPolygon::GetPositiveSelfSolidAngle(){
	re Solid=GetSolidAngle(CenterPoint.Position);
	if (Solid<0) {
		Solid+=TWO*PI2;
	}
	return Solid;
}

VortPolygon* VortPolygon::GetTopmostSibling(){
	if (pSibling==this) {
		return this;
	} else {
		pSibling=pSibling->GetTopmostSibling();
		return pSibling;
	}
}

int VortPolygon::GetNPoints(){
	return PointList.size();
}

VortPoint& VortPolygon::GetPoint(int index){
	return *(PointList[index].pPoint);
}

Vector3D& VortPolygon::GetCenterPosition(){
	return CenterPoint.Position;
}

Vector3D& VortPolygon::GetNormal(){
	return Normal;
}

void VortPolygon::EliminateDegenerateEdges(){
	int Size=PointList.size();
	for (int i=0; i<Size; i++) {
		int inext=(i+1)%Size;
		if (PointList[i]==PointList[inext]) {
			PointList.erase(PointList.begin()+i);
			Size--;
			i--;
		}
	}
}

int VortPolygon::GetIndex(const VortPointReference& Ref){
	int res=-1;
	int Size=PointList.size();
		
	for (int i=0; i<Size; i++) {
		if (PointList[i]==Ref) {
			return i;
		}
	}
	
	return res;
}


VortPointReference& VortPolygon::GetPointReference(int index){
	return PointList[index];
}
